﻿#region Disclaimer/Info
///////////////////////////////////////////////////////////////////////////////////////////////////
// MetaCollection
// 
// MetaCollection is a system to provide a standard structure, permissions and metadata service
// for content organization.
//
// This project is hosted at Google Code at http://code.google.com/p/metacollection/
//
// This project is licensed under the BSD license.  See the License.txt file for more information.
///////////////////////////////////////////////////////////////////////////////////////////////////
#endregion

using System;
using System.Linq;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Xml;
using System.Xml.Serialization;

namespace MetaCollection.Framework.Model
{
	/// <summary>
	/// A Model Property is the specification for a single property on an item.
	/// </summary>
	public interface IModelProperty
	{
		/// <summary>
		/// Name of this property.
		/// </summary>
		/// <remarks>Used internally as the name of the property.  Should contain no special chars or spaces to
		/// keep things simple.</remarks>
		/// <example>FirstName</example>
		string Name { get; set; }
		/// <summary>
		/// Extended or Base Property.
		/// </summary>
		/// <remarks>Base properties are defined as part of the IItemInstance.  Any base property can still have 
		/// restrictions or interface hints applied to it.  Extended properties are persisted as part of the IItemProperty
		/// colleciton.</remarks>
		bool IsExtended { get; set; }
		/// <summary>
		/// Indicates if this field allows null values.
		/// </summary>
		bool Required { get; set; }
		/// <summary>
		/// A regular expression used to validate the value of this property.
		/// </summary>
		/// <example>^[A-Za-z0-9]{2,256}$</example>
		Regex Validation { get; set; }
		/// <summary>
		/// Comma separated list of valid values for this property.
		/// </summary>
		/// <example>Option One, Option Two, Option Three</example>
		string ValidValues { get; set; }
		/// <summary>
		/// The type of form control that should be used to present this item to a user.
		/// </summary>
		FormField DisplayType { get; set; }
		/// <summary>
		/// A textual label that is associated with this properties form control.
		/// </summary>
		/// <example>First Name</example>
		string Label { get; set; }
		/// <summary>
		/// Indicates if this item property should be displayed to a user for editing on a given item edit form.
		/// </summary>
		bool UserEditable { get; set; }
		/// <summary>
		/// An error message to use when this properties value does not pass the validation expression.
		/// </summary>
		/// <example>A First Name is required and must be 2 to 255 letters in length.</example>
		string ErrorMessage { get; set; }
		/// <summary>
		/// Search Index: Specifies whether and how a field should be indexed.
		/// </summary>
		IndexStyle Style { get; set; }
		/// <summary>
		/// Search Index: Specifies whether and how a field should have term vectors.
		/// </summary>
		IndexStore Store { get; set; }
		/// <summary>
		/// Search Index: Specifies whether and how a field should have term vectors.
		/// </summary>
		IndexVector Vector { get; set; }
	}
	/// <summary>
	/// A simple structure that implements IModelProperty
	/// </summary>
	public struct ModelProperty : IModelProperty
	{
		/// <summary>
		/// Name of this property.
		/// </summary>
		/// <remarks>Used internally as the name of the property.  Should contain no special chars or spaces to
		/// keep things simple.</remarks>
		/// <example>FirstName</example>
		[XmlAttribute]
		public string Name { get; set; }
		/// <summary>
		/// Extended or Base Property.
		/// </summary>
		/// <remarks>Base properties are defined as part of the IItemInstance.  Any base property can still have 
		/// restrictions or interface hints applied to it.  Extended properties are persisted as part of the IItemProperty
		/// colleciton.</remarks>
		[XmlAttribute]
		public bool IsExtended { get; set; }
		/// <summary>
		/// Indicates if this field allows null values.
		/// </summary>
		[XmlAttribute]
		public bool Required { get; set; }
		/// <summary>
		/// A regular expression used to validate the value of this property.
		/// </summary>
		/// <example>^[A-Za-z0-9]{2,256}$</example>
		[XmlIgnore]
		public Regex Validation { get; set; }
		/// <summary>
		/// The validation regular expression as a string.
		/// </summary>
		[XmlAttribute("ValidationExpression")]
		public string ValidationExpression { get { return (Validation == null ? null : Validation.ToString()); } set { Validation = (string.IsNullOrEmpty(value) ? null : new Regex(value)); } }
		/// <summary>
		/// Comma separated list of valid values for this property.
		/// </summary>
		/// <example>Option One, Option Two, Option Three</example>
		[XmlAttribute]
		public string ValidValues { get; set; }
		/// <summary>
		/// The type of form control that should be used to present this item to a user.
		/// </summary>
		[XmlAttribute]
		public FormField DisplayType { get; set; }
		/// <summary>
		/// A textual label that is associated with this properties form control.
		/// </summary>
		/// <example>First Name</example>
		[XmlAttribute]
		public string Label { get; set; }
		/// <summary>
		/// Indicates if this item property should be displayed to a user for editing on a given item edit form.
		/// </summary>
		[XmlAttribute]
		public bool UserEditable { get; set; }
		/// <summary>
		/// An error message to use when this properties value does not pass the validation expression.
		/// </summary>
		/// <example>A First Name is required and must be 2 to 255 letters in length.</example>
		[XmlAttribute]
		public string ErrorMessage { get; set; }
		/// <summary>
		/// Search Index: Specifies whether and how a field should be indexed.
		/// </summary>
		[XmlAttribute]
		public IndexStyle Style { get; set; }
		/// <summary>
		/// Search Index: Specifies whether and how a field should have term vectors.
		/// </summary>
		[XmlAttribute]
		public IndexStore Store { get; set; }
		/// <summary>
		/// Search Index: Specifies whether and how a field should have term vectors.
		/// </summary>
		[XmlAttribute]
		public IndexVector Vector { get; set; }
	}

	/// <summary>
	/// Form Field Control Types
	/// </summary>
	public enum FormField
	{
		Text,
		TextArea,
		Radio,
		CheckBox,
		Select,
		MultipleSelect
	}

	/// <summary>
	/// Specifies whether and how a field should be indexed.
	/// </summary>
	/// <remarks>Based on Lucene.NET indexing options</remarks>
	public enum IndexStyle
	{
		/// <summary>
		/// Index the field's value so it can be searched. An Analyzer will be used to tokenize
		/// and possibly further normalize the text before its terms will be stored in the index.
		/// This is useful for common text. 
		/// </summary>
		Analyzed,
		/// <summary>
		/// Same as Analyzed except No Norms.
		/// No norms means that index-time boosting and field length normalization will be disabled.
		/// </summary>
		AnalyzedNoNorms,
		/// <summary>
		/// Do not index the field value.
		/// This field can thus not be searched, but one can still access its contents provided it is still Stored
		/// </summary>
		No,
		/// <summary>
		/// Index the field's value without using an Analyzer, so it can be searched. 
		/// As no analyzer is used the value will be stored as a single term. This is useful for unique Ids like product numbers. 
		/// </summary>
		NotAnalyzed,
		/// <summary>
		/// Index the field's value without an Analyzer, and disable the storing of norms. 
		/// No norms means that index-time boosting and field length normalization will be disabled. The benefit
		/// is less memory usage as norms take up one byte per indexed field for every document in the index. Note
		/// that once you index a given field with norms enabled, disabling norms will have no effect. In other words,
		/// for NO_NORMS to have the above described effect on a field, all instances of that field must be indexed
		/// with NO_NORMS from the beginning. 
		/// </summary>
		NotAnalyzedNoNorms,
	}

	/// <summary>
	/// Specifies whether and how a field should be stored.
	/// </summary>
	/// <remarks>Based on Lucene.NET indexing options</remarks>
	public enum IndexStore
	{
		/// <summary>
		/// Store the field in the index with compression
		/// </summary>
		Compress,
		/// <summary>
		/// Store the field in the index.
		/// </summary>
		Yes,
		/// <summary>
		/// Do not store this field in the index.
		/// </summary>
		No
	}

	/// <summary>
	/// Specifies whether and how a field should have term vectors.
	/// </summary>
	/// <remarks>Based on Lucene.NET indexing options</remarks>
	public enum IndexVector
	{
		/// <summary>
		/// Store the term vectors of each document
		/// </summary>
		Yes,
		/// <summary>
		/// Do not store term vector information
		/// </summary>
		No,
		/// <summary>
		/// Store the term vector with token offset information
		/// </summary>
		WithOffsets,
		/// <summary>
		/// Store the term vector with token position information
		/// </summary>
		WithPositions,
		/// <summary>
		/// Store the term vector with token offset and position information
		/// </summary>
		WithPositionsOffsets
	}

	public static partial class Extensions
	{
		/// <summary>
		/// Converts an enumerable set of IModelProperty to a simple structure implementation of the interface
		/// </summary>
		/// <param name="itemSpecifications"></param>
		/// <returns>An enumerable collection of Model Property structures.</returns>
		public static IEnumerable<ModelProperty> ToStruct(this IEnumerable<IModelProperty> modelProp)
		{
			foreach (var prop in modelProp)
			{
				yield return prop.ToStruct();
			}
			yield break;
		}

		/// <summary>
		/// Converts an IModelProperty to a simple structure that implements the interface.
		/// </summary>
		/// <param name="spec">an IModelProperty to convert into the ModelProperty struct</param>
		/// <returns>a struct that implements IModelProperty</returns>
		public static ModelProperty ToStruct(this IModelProperty prop)
		{
			// if the item passed in is already an ModelProperty (struct) then return.
			if (prop is ModelProperty)
				return (ModelProperty)prop;

			ModelProperty modelProp = new ModelProperty();
			if (prop == null)
				return modelProp;
			modelProp.DisplayType = prop.DisplayType;
			modelProp.ErrorMessage = prop.ErrorMessage;
			modelProp.IsExtended = prop.IsExtended;
			modelProp.Label = prop.Label;
			modelProp.Name = prop.Name;
			modelProp.Required = prop.Required;
			modelProp.Store = prop.Store;
			modelProp.Style = prop.Style;
			modelProp.UserEditable = prop.UserEditable;
			modelProp.Validation = prop.Validation;
			modelProp.ValidValues = prop.ValidValues;
			modelProp.Vector = prop.Vector;
			return modelProp;
		}
	}
}
